﻿namespace Infra.Validation
{
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Represents the result of validation/s.
    /// </summary>
    public sealed class ValidationResult
    {
        private bool _dirty = true;
        private bool _valid;

        /// <summary>
        /// Gets or sets the messages list.
        /// </summary>
        /// <value>The messages.</value>
        public IList<ValidationMessage> Messages { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this <see cref="ValidationResult"/> is valid.
        /// </summary>
        /// <value><c>true</c> if valid; otherwise, <c>false</c>.</value>
        public bool Valid
        {
            get
            {
                //theoretically O(n) time but in pratice will be constant time
                //Still, no need for multiple traversals, Traverse only if it's changed
                if (_dirty)
                {
                    _valid = Messages.FirstOrDefault(msg => msg.IsStopper) == null;
                    _dirty = false;
                }

                return _valid;
            }
        }

        /// <summary>
        /// Gets the number of errors.
        /// </summary>
        /// <value>The number of errors.</value>
        public int NumberOfErrors
        {
            get { return this.Messages.Count; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidationResult"/> class.
        /// </summary>
        /// <param name="withMessages">The with messages.</param>
        internal ValidationResult(IEnumerable<ValidationMessage> withMessages)
        {
            this.Messages = this.Messages.Concat(withMessages).ToList();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidationResult"/> class.
        /// </summary>
        internal ValidationResult()
        {
            Messages = new List<ValidationMessage>();
        }

        /// <summary>
        /// Adds the error. 
        /// </summary>
        /// <param name="errorMessage">The error message.</param>
        public void AddError(string errorMessage)
        {
            _dirty = true;
            Messages.Add(new ErrorValidationMessage(errorMessage));
        }

        /// <summary>
        /// Adds the warning. 
        /// </summary>
        /// <param name="warningMessage">The warning message.</param>
        public void AddWarning(string warningMessage)
        {
            //No need to mark collection dirty since warnings never generate validation changes
            Messages.Add(new WarningValidationMessage(warningMessage));
        }

        /// <summary>
        /// Adds the generic message.
        /// </summary>
        /// <param name="genericMessage">The generic message.</param>
        public void AddGenericMessage(ValidationMessage genericMessage)
        {
            Messages.Add(genericMessage);
        }
    }
}
